package loppy.dynamics 
{
  import adobe.utils.CustomActions;
  import loppy.loppy_internal;
  import loppy.math.Vec2;
  
  public class PolygonShape extends Shape
  {
    use namespace loppy_internal;
    
    loppy_internal var localVertices :Vector.<Vec2> = new Vector.<Vec2>();
    loppy_internal var globalVertices:Vector.<Vec2> = new Vector.<Vec2>();
    loppy_internal var localNormals  :Vector.<Vec2> = new Vector.<Vec2>();
    loppy_internal var globalNormals :Vector.<Vec2> = new Vector.<Vec2>();
    loppy_internal var localCentroid :Vec2          = new Vec2();
    loppy_internal var globalCentroid:Vec2          = new Vec2();
    loppy_internal var radius        :Number        = 0.0;
    
    /**
     * 
     * @param	vertices Vertex array in counterclockwise order (x points right, y points up)
     * @param	config
     */
    public function PolygonShape(config:ShapeConfig) 
    {
      super(POLYGON, config);
    }
    
    public function setAsBox(width:Number, height:Number):PolygonShape
    {
      var hw:Number = 0.5 * width;
      var hh:Number = 0.5 * height;
      setVertices
      (
        [
          new Vec2( hw,  hh), 
          new Vec2(-hw,  hh), 
          new Vec2(-hw, -hh), 
          new Vec2( hw, -hh)
        ]
      );
      return this;
    }
    
    public function setVertices(vertices:Array):PolygonShape
    {
      localVertices  = new Vector.<Vec2>(vertices.length, true);
      globalVertices = new Vector.<Vec2>(vertices.length, true);
      localNormals   = new Vector.<Vec2>(vertices.length, true);
      globalNormals  = new Vector.<Vec2>(vertices.length, true);
      
      var radius:Number = Number.NEGATIVE_INFINITY;
      
      for (var i:int = 0, len:int = vertices.length; i < len; ++i)
      {
        localVertices [i] = new Vec2(vertices[i].x, vertices[i].y);
        globalVertices[i] = new Vec2();
        localNormals  [i] = new Vec2();
        globalNormals [i] = new Vec2();
        
        var r:Number = localVertices[i].length();
        if (r > radius) radius = r;
      }
      
      calculateCentroid();
      updateMass();
      
      return this;
    }
    
    private function calculateCentroid():void
    {
      var i:int, len:int;
      
      //calculate local vertex average
      var localAverage:Vec2 = Vec2.pool.get();
      localAverage.set(0.0, 0.0);
      for (i = 0, len = localVertices.length; i < len; ++i)
      {
        //calculate average vertex
        localAverage.addThis(localVertices[i]);
      }
      localAverage.mulThis(1.0 / localVertices.length); //average vertex is now calculated
      
      //calculate the centroid
      var area    :Number = 0.0;
      var side1   :Vec2   = Vec2.pool.get();
      var side2   :Vec2   = Vec2.pool.get();
      var oneThird:Number = 1.0 / 3.0;
      localCentroid.set(0.0, 0.0);
      for (i = 0, len = localVertices.length; i < len; ++i)
      {
        //2 sides of one triangle (from the local average)
        var vert1:Vec2 = localVertices[ i                       ];
        var vert2:Vec2 = localVertices[(i + 1 < len)?(i + 1):(0)];
        side1.copy(vert1).subThis(localAverage);
        side2.copy(vert2).subThis(localAverage);
        
        //add to total area
        var triangleArea:Number = 0.5 * side1.cross(side2);
        area += triangleArea;
        
        //added weighted triangle center to local centroid
        localCentroid.x += triangleArea * oneThird * (vert1.x + vert2.x + localAverage.x);
        localCentroid.y += triangleArea * oneThird * (vert1.y + vert2.y + localAverage.y);
      }
      localCentroid.mulThis(1.0 / area); //centroid is now calculated
      
      Vec2.pool.put(localAverage);
      Vec2.pool.put(side1);
      Vec2.pool.put(side2);
    }
    
    override internal function updateMass():void 
    {
      //TODO
      
      
      inverseMass    = 1.0 / mass;
      inverseInertia = 1.0 / intertia;
    }
    
    override internal function updateTransform():void 
    {
      super.updateTransform();
      
      for (var i:int = 0, len:int = localVertices.length; i < len; ++i)
      {
        //calculate global positions of vertices
        globalVertices[i]
          .copy(localVertices[i])
          .rotateThis(globalRotation)
          .addThis(globalPosition);
        
        //calculate local normals
        localNormals[i]
          .copy(localVertices[(i + 1 < len)?(i + 1):(0)])
          .subThis(localVertices[i])
          .rotateCwRightAngleThis()
          .normalizeThis();
        
        //calculate global normals
        globalNormals[i]
          .copy(localNormals[i])
          .rotateThis(globalRotation);
      }
    }
    
    override internal function updateProxy():void 
    {
      proxy.minX = proxy.minY = Number.POSITIVE_INFINITY;
      proxy.maxX = proxy.maxY = Number.NEGATIVE_INFINITY;
      
      for (var i:int = 0, len:int = globalVertices.length; i < len; ++i)
      {
        if (globalVertices[i].x < proxy.minX) proxy.minX = globalVertices[i].x;
        if (globalVertices[i].x > proxy.maxX) proxy.maxX = globalVertices[i].x;
        if (globalVertices[i].y < proxy.minY) proxy.minY = globalVertices[i].y;
        if (globalVertices[i].y > proxy.maxY) proxy.maxY = globalVertices[i].y;
      }
    }
  }
}